home *** CD-ROM | disk | FTP | other *** search
/ Megahits 5 / Megahits 5 (1994)(GTI - Rhein-Main-Soft)(DE)(Disc 2 of 2)[!].iso / archive / conv / buildmpeg.lha / BuildMPEG.ifx next >
Text File  |  1994-03-22  |  16KB  |  628 lines

  1. /*
  2.  *  BuildMPEG, compile a list of images into an MPEG stream
  3.  *  using the Stanford codec
  4.  *
  5.  *  history
  6.  *
  7.  *  18/04/1994  fixed problems with odd sized pictures
  8.  *
  9.  *  16/04/1994  fixed a few bugs
  10.  *
  11.  *  13/04/1994  added requester that shows why the codec might have failed
  12.  *              previously it just guessed memory problems
  13.  *              turned redraw off for loading and preprocessing
  14.  *
  15.  *  03/03/1994  added checks to avoid frozen script when the codec never started
  16.  *              (bad installation or memory problems)
  17.  *
  18.  *  28/01/1994  allows selection of a filter script to preprocess source frames
  19.  *              also cosmetic changes
  20.  *
  21.  *  02/01/1994  tries to handle user abort
  22.  *              should definitely work if aborted through 'waiting for activity'
  23.  *              period.
  24.  *
  25.  *  01/01/1994  first version
  26.  *
  27.  *  Copyright Michael van Elst 1994
  28.  *
  29.  *  This program is freely distributable, but copyrighted by me. This means
  30.  *  that you can copy it freely as long as you don't ask for any more money
  31.  *  than a nominal fee for copying. This program may be put on PD disks,
  32.  *  especially on Fred Fish's AmigaLibDisks.
  33.  *  This program cannot be used for commercial purposes without permission
  34.  *  from the author. The author can not be made responsible for any damage
  35.  *  which is caused by using this program.
  36.  *  
  37.  *  This notice applies to the AREXX script, my changes to the MPEG codec
  38.  *  done by the Portable Video Research Group at Stanford and the resulting
  39.  *  executable contained in the BuildMPEG archive.
  40.  */
  41.  
  42. OPTIONS RESULTS
  43.  
  44. /*logging='>"con:3/12/500/400/MPEG Log/AUTO/WAIT/CLOSE/INACTIVE"'*/
  45. logging=''
  46. profiling='sc:c/lprof -t5'
  47. /*profiling=''*/
  48.  
  49. cleanup = nothing
  50. SIGNAL ON BREAK_C
  51.  
  52. CALL ADDLIB("rexxsupport.library",0,-30,0)
  53.  
  54. CALL DoRequesters
  55.  
  56. CALL MakeOptions
  57.  
  58. CALL MainLoop
  59.  
  60. EXIT
  61.  
  62. /*-------------------------------------------------------------------------
  63.  *
  64.  * auxiliary functions
  65.  *
  66.  *-------------------------------------------------------------------------*/
  67.  
  68. /*
  69.  * check if argument is a positive integer
  70.  * if not then return default
  71.  */
  72. NumDefault:
  73.     procedure
  74.     parse arg input,low,high,default
  75.  
  76.     if ~datatype(input,'w') | input<low | input>high
  77.         then return default
  78.         else return input
  79.  
  80. /*
  81.  * display an information requester with no special symbols in strings
  82.  */
  83. MyNotify:
  84.     procedure
  85.     parse arg prompt
  86.  
  87.     string = ""
  88.     do until i=0
  89.         i = pos('%',prompt)
  90.         if i>0 then do
  91.             string = string || left(prompt,i) || '%'
  92.             prompt = substr(prompt,i+1)
  93.             end
  94.         else
  95.             string = string || prompt
  96.     end
  97.             
  98.     requestnotify string
  99.     return
  100.  
  101. /*
  102.  * ensure reasonable defaults
  103.  */
  104. EnsureDefaults:
  105.     bitrate    = numdefault(bitrate,0,100000,1200)
  106.     ffirst     = numdefault(ffirst,0,999999,1)
  107.     flast      = numdefault(flast,0,999999,10)
  108.     finterval  = numdefault(finterval,0,99,3)
  109.     ginterval  = numdefault(ginterval,0,99,2)
  110.     framerate  = numdefault(framerate,1,8,3)
  111.     targetsize = numdefault(targetsize,0,10000000,0)
  112.     xingflag   = numdefault(xingflag,0,1,0)
  113.     msdiameter = numdefault(msdiameter,1,15,15)
  114.     intramode  = numdefault(intramode,0,1,0)
  115.     precisedct = numdefault(precisedct,0,1,0)
  116.     telescope  = numdefault(telescope,0,1,1)
  117.     bounding   = numdefault(bounding,0,1,0)
  118.     mvpredict  = numdefault(mvpredict,0,1,0)
  119.     quantizer  = numdefault(quantizer,0,31,0)
  120.     return
  121.  
  122. /*
  123.  * format a positive integer to have at least n digits
  124.  */
  125. MakeDigits:
  126.     procedure
  127.     arg v,n
  128.  
  129.     l = length(v)
  130.     do while l<n
  131.         v = "0"v
  132.         l = l+1
  133.     end
  134.  
  135.     return v
  136.     
  137. /*
  138.  * trim quotes from a string
  139.  */
  140. TrimQuotes:
  141.     procedure
  142.     parse arg in
  143.  
  144.     l = length(in)
  145.  
  146.     if l>1 & left(in,1)='"' & right(in,1)='"'
  147.         then out = substr(in,2,l-2)
  148.         else out = in
  149.  
  150.     return out
  151.  
  152. /*-------------------------------------------------------------------------
  153.  *
  154.  * parameter requesters
  155.  *
  156.  *-------------------------------------------------------------------------*/
  157.  
  158. DoRequesters:
  159.  
  160. /*
  161.  * fetch last parameters
  162.  */
  163. inpattern  = GETCLIP('MPEG_In')
  164. ffirst     = GETCLIP('MPEG_FirstFrame')
  165. flast      = GETCLIP('MPEG_LastFrame')
  166. outfile    = GETCLIP('MPEG_Out')
  167. finterval  = GETCLIP('MPEG_FInterval')
  168. ginterval  = GETCLIP('MPEG_GInterval')
  169. framerate  = GETCLIP('MPEG_Framerate')
  170. bitrate    = GETCLIP('MPEG_Bitrate')
  171. targetsize = GETCLIP('MPEG_Targetsize')
  172. xingflag   = GETCLIP('MPEG_Xingflag')
  173. cscript    = GETCLIP('MPEG_Controlscript')
  174. msdiameter = GETCLIP('MPEG_MSDiameter')
  175. intramode  = GETCLIP('MPEG_Intramode')
  176. precisedct = GETCLIP('MPEG_PreciseDCT')
  177. telescope  = GETCLIP('MPEG_MVTelescoping')
  178. bounding   = GETCLIP('MPEG_DMVBounding')
  179. mvpredict  = GETCLIP('MPEG_MVPrediction')
  180. quantizer  = GETCLIP('MPEG_Quantization')
  181. fscript    = GETCLIP('MPEG_FilterScript')
  182.  
  183. CALL EnsureDefaults
  184.  
  185. fpsstring = '23.51fps 24fps 25fps 25.50fps 30fps 50fps 59.94fps 60fps'
  186. if framerate>1 then
  187.     gadstring = subword(fpsstring,framerate,words(fpsstring)-framerate+1) || ,
  188.                 " " || subword(fpsstring,1,framerate-1)
  189. else
  190.     gadstring = fpsstring
  191. gadstring = translate(gadstring,"/"," ")
  192.  
  193. Gadget.1  = 'S/125/20/Input Pattern:/'inpattern
  194. Gadget.2  = 'I/330/20/From:/'ffirst
  195. Gadget.3  = 'I/430/20/To:/'flast
  196. Gadget.4  = 'S/125/35/Output Filename:/'outfile
  197. Gadget.5  = 'I/125/50/Frame Interval:/'finterval
  198. Gadget.6  = 'I/125/65/Group Interval:/'ginterval
  199. Gadget.7  = 'C/430/35/Frame rate:/8/'gadstring
  200. Gadget.8  = 'L/348/38/1/1/Frame rate:'
  201. Gadget.9  = 'I/430/50/Bit rate:/'bitrate
  202. Gadget.10 = 'I/430/65/Target size:/'targetsize
  203. Gadget.11 = 'X/85/82/×ING Override/'xingflag
  204. Gadget.12 = 'X/255/82/Query Advanced Options.../0'
  205.  
  206. Extras.1  = 'X/20/20/DC intraframe mode/'intramode
  207. Extras.2  = 'X/20/35/Use Precise DCT/'precisedct
  208. Extras.3  = 'S/130/80/Control Script:/'cscript
  209. Extras.4  = 'X/280/20/Motion Vector Telescoping/'telescope
  210. Extras.5  = 'X/280/35/Dynamic Motion Vector Bounding/'bounding
  211. Extras.6  = 'X/280/50/Motion Vector prediction/'mvpredict
  212. Extras.7  = 'I/420/65/Search diameter:/'msdiameter
  213. Extras.8  = 'I/420/80/Quantization:/'quantizer
  214. Extras.9  = 'S/130/65/IFX Filter:/'fscript
  215.  
  216. ComplexRequest '"MPEG Compiler"' 12 Gadget 540 120
  217. IF rc ~= 0 then EXIT
  218.  
  219. /* fetch parameters back from requester */
  220. inpattern  = result.1
  221. ffirst     = result.2
  222. flast      = result.3
  223. outfile    = result.4
  224. finterval  = result.5
  225. ginterval  = result.6
  226. fpsindex   = result.7
  227. bitrate    = result.9
  228. targetsize = result.10
  229. xingflag   = result.11
  230. advanced   = result.12
  231.  
  232. framerate  = (fpsindex + (framerate-1)) // words(fpsstring) + 1
  233.  
  234. if advanced then do
  235.     ComplexRequest '"MPEG Advanced Options"' 9 Extras 540 120
  236.     if rc ~= 0 then EXIT
  237.     /* fetch parameters back from requester */
  238.     intramode  = result.1
  239.     precisedct = result.2
  240.     cscript    = result.3
  241.     telescope  = result.4
  242.     bounding   = result.5
  243.     mvpredict  = result.6
  244.     msdiameter = result.7
  245.     quantizer  = result.8
  246.     fscript    = result.9
  247. end
  248.  
  249. CALL EnsureDefaults
  250.  
  251. CALL SETCLIP('MPEG_In',            inpattern)
  252. CALL SETCLIP('MPEG_FirstFrame',    ffirst)
  253. CALL SETCLIP('MPEG_LastFrame',     flast)
  254. CALL SETCLIP('MPEG_Out',           outfile)
  255. CALL SETCLIP('MPEG_FInterval',     finterval)
  256. CALL SETCLIP('MPEG_GInterval',     ginterval)
  257. CALL SETCLIP('MPEG_Framerate',     framerate)
  258. CALL SETCLIP('MPEG_Bitrate',       bitrate)
  259. CALL SETCLIP('MPEG_Targetsize',    targetsize)
  260. CALL SETCLIP('MPEG_Xingflag',      xingflag)
  261. CALL SETCLIP('MPEG_Controlscript', cscript)
  262. CALL SETCLIP('MPEG_MSDiameter',    msdiameter)
  263. CALL SETCLIP('MPEG_Intramode',     intramode)
  264. CALL SETCLIP('MPEG_PreciseDCT',    precisedct)
  265. CALL SETCLIP('MPEG_MVTelescoping', telescope)
  266. CALL SETCLIP('MPEG_DMVBounding',   bounding)
  267. CALL SETCLIP('MPEG_MVPrediction',  mvpredict)
  268. CALL SETCLIP('MPEG_Quantization',  quantizer)
  269. CALL SETCLIP('MPEG_FilterScript',  fscript)
  270.  
  271. /*
  272.  * split input pattern
  273.  */
  274.  
  275. csep = pos(':',inpattern)
  276. ssep = lastpos('/',inpattern)
  277. if ssep>0 & ssep>csep then do
  278.         directory  = left(inpattern,ssep)
  279.         infilename = substr(inpattern,ssep+1)
  280.         end
  281. else if csep>0 & csep>ssep then do
  282.         directory  = left(inpattern,csep)
  283.         infilename = substr(inpattern,csep+1)
  284.         end
  285. else do
  286.         directory  = ""
  287.         infilename = inpattern
  288. end
  289.  
  290. csep = pos('%',infilename)
  291. if csep>0 then do
  292.     ssep=csep+1
  293.     do while substr(infilename,ssep,1)='%'
  294.         ssep = ssep+1
  295.     end
  296.     inprefix = left(infilename,csep-1)
  297.     insuffix = substr(infilename,ssep)
  298.     indigits = ssep-csep
  299.     end
  300. else do
  301.     inprefix = infilename
  302.     insuffix = ""
  303.     indigits = 1
  304. end
  305.  
  306. return
  307.  
  308. /*-------------------------------------------------------------------------
  309.  *
  310.  * run a user command or script
  311.  *
  312.  *-------------------------------------------------------------------------*/
  313.  
  314. UserScript:
  315.  
  316. if pos(':',fscript)>0 then
  317.     filterfile = fscript
  318. else
  319.     filterfile = "IMAGEFX:mpegfilters/"fscript
  320.  
  321. if open('filter',filterfile,'r') then do
  322.     rx quiet filterfile
  323.     call close('filter')
  324.     end
  325. else if open('filter',filterfile'.ifx','r') then do
  326.     rx quiet filterfile".ifx"
  327.     call close('filter')
  328.     end
  329. else
  330.     rxs fscript
  331.  
  332. return
  333.  
  334. /*-------------------------------------------------------------------------
  335.  *
  336.  * automatically preformat for XING files
  337.  *
  338.  *-------------------------------------------------------------------------*/
  339.  
  340. XingPreProc:
  341.  
  342. GetMain ; if result="" then EXIT
  343. parse VAR result name width height depth .
  344. if width ~= 160 | height ~= 120 then
  345.     scale 160 120
  346.  
  347. return
  348.  
  349.  
  350. /*-------------------------------------------------------------------------
  351.  *
  352.  * create command line options for Stanford CODEC
  353.  *
  354.  *-------------------------------------------------------------------------*/
  355.  
  356. MakeOptions:
  357.  
  358. command = "IMAGEFX:mpeg/mpeg"
  359. opts    = "-PF" "-a" ffirst "-b" flast
  360.  
  361. check=statef(command)
  362. parse var check typ len blocks perm .
  363. if typ ~= "FILE" | substr(perm,7,1) ~= 'E' then do
  364.     call MyNotify("Sorry, can't find "command" or wrong permissions.")
  365.     EXIT
  366. end
  367.  
  368. if xingflag then
  369.     opts = opts "-XING"
  370. else do
  371.     if cscript ~= "" then do
  372.         if pos(':',cscript)>0 then
  373.             controlfile = cscript
  374.         else
  375.             controlfile = "IMAGEFX:mpegcontrol/"cscript
  376.         command = command '<"'controlfile'"'
  377.         opts = opts "-o"
  378.     end
  379.     if ~telescope then opts = opts "-NVNT"
  380.     if bounding   then opts = opts "-DMVB"
  381.     if intramode  then opts = opts "-4"
  382.     if mvpredict  then opts = opts "-c"
  383.     if precisedct then opts = opts "-y"
  384.     if finterval>0  then opts = opts "-f" finterval
  385.     if ginterval>0  then opts = opts "-g" ginterval
  386.     if msdiameter>0 then opts = opts "-i" msdiameter
  387.     if bitrate>0    then opts = opts "-r" bitrate*1024
  388.     if framerate>0  then opts = opts "-p" framerate
  389.     if quantizer>0  then opts = opts "-q" quantizer
  390.     if targetsize>0 then opts = opts "-x" targetsize*8192
  391. end
  392.  
  393. id = random(100,999,time('s'))
  394. pipename = "PIPE:"address()"."id"="
  395. portname = "IFX_BuildMPEGServer."id
  396. statfile = 'T:CODE.return_status.'id
  397.  
  398. opts = opts "-REXX" portname||":"||mpeg
  399.  
  400. if outfile = "" then do
  401.     requestnotify "Sorry, you must given an output filename"
  402.     EXIT
  403.     end
  404.  
  405. /*
  406.  * preload first frame to determine size or
  407.  * to check size against XING values
  408.  */
  409. lastbuf  = directory||inprefix||makedigits(ffirst,indigits)||insuffix
  410. redraw off
  411. loadbuffer lastbuf FORCE
  412. if rc>0 then do
  413.     redraw on
  414.     requestnotify "Failed to preload first frame"
  415.     EXIT 10
  416.     end
  417. CALL UserScript
  418. if xingflag then CALL XingPreProc
  419. redraw on
  420.  
  421. GetMain ; if result="" then EXIT
  422. parse VAR result name width height depth .
  423. /* round size up, YUVSPLIT does the same */
  424. if (width // 2) > 0 then width = width + 1
  425. if (height // 2) > 0 then height = height + 1
  426.  
  427. if xingflag then
  428. do
  429.     if width ~= 160 | height ~= 120 then do
  430.         requestnotify "XING frames need to be of size 160x120 pixel"
  431.         EXIT
  432.         end
  433. end
  434. else
  435.     opts = opts "-h" width "-v" height
  436.  
  437. opts = opts "-s" '"'outfile'"' '"'pipename'"'
  438.  
  439. mpegcommand = command opts
  440.  
  441. return
  442.  
  443. /*-------------------------------------------------------------------------
  444.  *
  445.  * run the codec program and feed it with data
  446.  *
  447.  *-------------------------------------------------------------------------*/
  448.  
  449. MainLoop:
  450.  
  451. LockInput
  452. Undo Off
  453.  
  454. cleanup = "restoreifx"
  455.  
  456. nullptr  = '00000000'x
  457.  
  458. port = openport(portname);
  459. if port = nullptr then do
  460.     call MyNotify("can't create server port '" portname "'")
  461.     exit
  462. end
  463.  
  464. packet  = "xx"
  465. cleanup = "flushcodec"
  466. CALL SETCLIP('MPEG_PipeName',"")
  467. message "Running CODEC in the background"
  468. pre      = 'failat 99'
  469. post1    = 'get RC >'statfile
  470. post2    = 'rx "address '''portname''' x FAIL"'
  471. sep      = '+'x2c('0a')
  472. address command "run" logging pre sep profiling mpegcommand sep post1 sep post2
  473. if rc>0 then do
  474.     call MyNotify("can't create background process")
  475.     exit
  476. end
  477. framenumber = 1
  478.  
  479. do until subcommand = "FAIL"
  480.     cleanup = "flushcodec"
  481.     message "Waiting for activity on frame "framenumber
  482.     call waitpkt portname
  483.     do until packet=nullptr
  484.         packet = getpkt(portname);
  485.         if packet ~= nullptr then
  486.         do
  487.             command = getarg(packet,0)
  488.  
  489.             call reply packet, 0            /* should be atomic */
  490.             packet = nullptr
  491.  
  492.             parse var command prefix subcommand filespec x y .
  493.             CALL SETCLIP('MPEG_PipeName',filespec)
  494.             select
  495.                 when subcommand = "LoadMem" then do
  496.                     /* filespec is something like:  PIPE:ident.random=framenr.Y */
  497.                     csep = lastpos('=',filespec)
  498.                     ssep = lastpos('.',filespec)
  499.                     filetype = substr(filespec,ssep+1,1)
  500.                     framenumber = substr(filespec,csep+1,ssep-csep-1)
  501.                     framenumber = makedigits(framenumber,indigits)
  502.  
  503.                     message "pushing "filetype" data for frame "framenumber
  504.  
  505.                     newbuf = directory||inprefix||framenumber||insuffix
  506.                     if lastbuf ~= newbuf then do
  507.                         redraw off
  508.                         loadbuffer newbuf FORCE
  509.                         if rc>0 then do
  510.                             requestnotify "Failed to read frame"||framenumber
  511.                             SIGNAL BREAK_C
  512.                         end
  513.                         CALL UserScript
  514.                         if xingflag then CALL XingPreProc
  515.                         redraw on
  516.                         lastbuf = newbuf
  517.                     end
  518.                     GetMain ; if result="" then SIGNAL BREAK_C
  519.                     parse VAR result name width height depth .
  520.                     /* round size up, YUVSPLIT does the same */
  521.                     if (width // 2) > 0 then width = width + 1
  522.                     if (height // 2) > 0 then height = height + 1
  523.                     if filetype ~= 'Y' then do
  524.                         width  = width/2
  525.                         height = height/2
  526.                     end
  527.  
  528.                     /* this check fails when the framesize is not constant
  529.                      * or a load failed (then width=height=0)
  530.                      */
  531.                     if width ~= x | height ~= y then do
  532.                         /* then we push an empty file which will
  533.                          * make the codec abort
  534.                          */
  535.                            filename = trimquotes(filespec)
  536.                         call open('push',filename,'w')
  537.                         call writeln('push',"STOP")  /* some dummy data */
  538.                         call close('push')
  539.                         end
  540.                     else
  541.                         savebufferas YUVSPLIT filespec filetype
  542.                 end
  543.                 when subcommand = "EXIT" then
  544.                     cleanup = "restoreifx"
  545.                 when subcommand = "FAIL" then do
  546.                     end
  547.                 otherwise
  548.                     call MyNotify("unknown command" subcommand)
  549.             end
  550.         end
  551.     end
  552. end
  553.  
  554. code="Out of memory ?"
  555. if open('result',statfile,'r') then do
  556.     code = readln('result')
  557.     call close('result')
  558.     select
  559.         when code=0  then code=0
  560.         when code=1  then code="Input values out of bounds."
  561.         when code=2  then code="Huffman decoder finds bad code."
  562.         when code=3  then code="Undefined value in encoder."
  563.         when code=4  then code="Error found in Marker."
  564.         when code=5  then code="Cannot initialize MPEG stream."
  565.         when code=6  then code="No recovery mode specified."
  566.         when code=7  then code="End of file unexpected."
  567.         when code=8  then code="Bad marker structure."
  568.         when code=9  then code="Cannot write output."
  569.         when code=10 then code="Cannot read input."
  570.         when code=11 then code="System parameter Error."
  571.         when code=12 then code="Memory allocation failure."
  572.         otherwise         code="General failure."
  573.     end
  574. end
  575. if code~=0 then call MyNotify("MPEG encoder failed. "||code)
  576. message "done"
  577.  
  578. call closeport(port);
  579. address command "delete force file " statfile
  580.  
  581. UnlockInput
  582. Undo On
  583.  
  584. return
  585.  
  586. /*-------------------------------------------------------------------------
  587.  *
  588.  * cleaning up
  589.  *
  590.  *-------------------------------------------------------------------------*/
  591.  
  592. BREAK_C:
  593.  
  594. /*
  595.  * beware.. we can't handle another break_c at this point
  596.  */
  597.  
  598. select
  599.     when cleanup="restoreifx" then do
  600.         call closeport(port)
  601.         UnlockInput
  602.         Undo On
  603.         end
  604.     when cleanup="flushcodec" then do
  605.         call closeport(port)            /* let all requests fail immediatly */
  606.         if (filespec ~= "") then do
  607.             filename = trimquotes(filespec)
  608.             if ~open('tryme',filename,'r') then do
  609.                 if open('push',filename,'w') then do
  610.                     call writeln('push',"STOP")  /* some dummy data */
  611.                     call close('push')
  612.                     end
  613.                 else
  614.                     requestnotify "pushing data failed. Possible Hangup."
  615.                 end
  616.             else do
  617.                 call close('tryme')
  618.             end
  619.         end
  620.         UnlockInput
  621.         Undo On
  622.         end
  623.     otherwise
  624.         NOP
  625. end
  626.  
  627. EXIT
  628.